package com.summerbird.mallchat.common.chat.service.impl;

import cn.hutool.core.lang.Pair;
import com.summerbird.mallchat.common.chat.dao.*;
import com.summerbird.mallchat.common.chat.domain.dto.RoomBaseInfo;
import com.summerbird.mallchat.common.chat.domain.entity.*;
import com.summerbird.mallchat.common.chat.domain.enums.GroupRoleAPPEnum;
import com.summerbird.mallchat.common.chat.domain.enums.GroupRoleEnum;
import com.summerbird.mallchat.common.chat.domain.enums.HotFlagEnum;
import com.summerbird.mallchat.common.chat.domain.enums.RoomTypeEnum;
import com.summerbird.mallchat.common.chat.domain.vo.request.ChatMessageMemberReq;
import com.summerbird.mallchat.common.chat.domain.vo.request.GroupAddReq;
import com.summerbird.mallchat.common.chat.domain.vo.request.member.MemberAddReq;
import com.summerbird.mallchat.common.chat.domain.vo.request.member.MemberDelReq;
import com.summerbird.mallchat.common.chat.domain.vo.request.member.MemberReq;
import com.summerbird.mallchat.common.chat.domain.vo.response.ChatMemberListResp;
import com.summerbird.mallchat.common.chat.domain.vo.response.ChatRoomResp;
import com.summerbird.mallchat.common.chat.domain.vo.response.MemberResp;
import com.summerbird.mallchat.common.chat.service.ChatService;
import com.summerbird.mallchat.common.chat.service.RoomAppService;
import com.summerbird.mallchat.common.chat.service.RoomService;
import com.summerbird.mallchat.common.chat.service.adapter.MemberAdapter;
import com.summerbird.mallchat.common.chat.service.cache.*;
import com.summerbird.mallchat.common.chat.service.strategy.msg.AbstractMsgHandler;
import com.summerbird.mallchat.common.chat.service.strategy.msg.MsgHandlerFactory;
import com.summerbird.mallchat.common.common.annotation.RedissonLock;
import com.summerbird.mallchat.common.common.domain.vo.req.CursorPageBaseReq;
import com.summerbird.mallchat.common.common.domain.vo.resp.CursorPageBaseResp;
import com.summerbird.mallchat.common.common.event.GroupMemberAddEvent;
import com.summerbird.mallchat.common.common.exception.GroupErrorEnum;
import com.summerbird.mallchat.common.common.utils.AssertUtil;
import com.summerbird.mallchat.common.user.dao.UserDao;
import com.summerbird.mallchat.common.user.domain.entity.User;
import com.summerbird.mallchat.common.user.domain.enums.RoleEnum;
import com.summerbird.mallchat.common.user.service.IRoleService;
import com.summerbird.mallchat.common.user.service.adapter.ChatAdapter;
import com.summerbird.mallchat.common.user.service.adapter.FriendAdapter;
import com.summerbird.mallchat.common.user.service.adapter.RoomAdapter;
import com.summerbird.mallchat.common.user.service.cache.UserCache;
import com.summerbird.mallchat.common.user.service.cache.UserInfoCache;
import com.summerbird.mallchat.common.user.service.impl.PushService;
import com.summerbird.mallchat.common.websocket.domain.vo.response.ChatMemberResp;
import com.summerbird.mallchat.common.websocket.domain.vo.response.WSBaseResp;
import com.summerbird.mallchat.common.websocket.domain.vo.response.WSMemberChange;
import com.summerbird.mallchat.common.websocket.service.adapter.WebSocketAdapter;
import jodd.util.CollectionUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.data.redis.core.ZSetOperations;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;

@Slf4j
@Service
public class RoomAppServiceImpl implements RoomAppService {

    @Autowired
    private HotRoomCache hotRoomCache;
    @Autowired
    private ContactDao contactDao;
    @Autowired
    private RoomCache roomCache;
    @Autowired
    private RoomGroupCache roomGroupCache;
    @Autowired
    private RoomFriendCache roomFriendCache;
    @Autowired
    private UserInfoCache userInfoCache;
    @Autowired
    private UserCache userCache;
    @Autowired
    private MessageDao messageDao;
    @Autowired
    private GroupMemberDao groupMemberDao;
    @Autowired
    private UserDao userDao;
    @Autowired
    private RoomService roomService;
    @Autowired
    private ChatService chatService;
    @Autowired
    private IRoleService iRoleService;
    @Autowired
    private PushService pushService;
    @Autowired
    private GroupMemberCache groupMemberCache;
    @Autowired
    private ApplicationEventPublisher applicationEventPublisher;

    @Override
    public CursorPageBaseResp<ChatRoomResp> getContactPage(CursorPageBaseReq request, Long uid) {
        // 查出用户要展示的会话列表
        CursorPageBaseResp<Long> page;
        // 用户登录
        if(Objects.nonNull(uid)){
            Double hotEnd = getCursorOrNull(request.getCursor());
            Double hotStart = null;
            // 用户基础会话
            CursorPageBaseResp<Contact> contactPage = contactDao.getContactPage(uid, request);
            List<Long> baseRoomIds = contactPage.getList().stream().map(Contact::getRoomId).collect(Collectors.toList());
            if(Objects.nonNull(contactPage.getCursor())){
                hotStart = getCursorOrNull(contactPage.getCursor());
            }
            // 热门房间
            Set<ZSetOperations.TypedTuple<String>> roomRange = hotRoomCache.getRoomRange(hotStart, hotEnd);
            List<Long> hotRoomIds = roomRange.stream().map(ZSetOperations.TypedTuple::getValue).filter(Objects::nonNull).map(Long::parseLong).collect(Collectors.toList());
            // 基础会话和热门房间合并
            baseRoomIds.addAll(hotRoomIds);
            page = CursorPageBaseResp.init(contactPage, baseRoomIds);
        }
        // 用户未登录，只查全局房间
        else {
            CursorPageBaseResp<Pair<Long, Double>> roomCursorPage = hotRoomCache.getRoomCursorPage(request);
            List<Long> roomIds = roomCursorPage.getList().stream().map(Pair::getKey).collect(Collectors.toList());
            page = CursorPageBaseResp.init(roomCursorPage, roomIds);
        }
        // 最后组装会话信息（名称，头像，未读数等）
        List<ChatRoomResp> result = buildContactResp(uid, page.getList());
        return CursorPageBaseResp.init(page, result);
    }

    private List<ChatRoomResp> buildContactResp(Long uid, List<Long> roomIds) {
        // 表情和头像
        Map<Long, RoomBaseInfo> roomBaseInfoMap = getRoomBaseInfoMap(uid, roomIds);
        // 最后一条消息
        List<Long> lastMsgIds = roomBaseInfoMap.values().stream().map(RoomBaseInfo::getLastMsgId).collect(Collectors.toList());
        List<Message> messages = CollectionUtils.isEmpty(lastMsgIds) ? new ArrayList<>() : messageDao.listByIds(lastMsgIds);
        Map<Long, Message> msgMap = messages.stream().collect(Collectors.toMap(Message::getId, Function.identity()));
        Map<Long, User> lastMsgUserMap = userInfoCache.getBatch(msgMap.values().stream().map(Message::getFromUid).collect(Collectors.toList()));
        // TODO: 消息未读数
        // 组装结果
        return roomBaseInfoMap.values().stream()
                .map(roomBaseInfo -> {
                    ChatRoomResp resp = new ChatRoomResp();
                    resp.setRoomId(roomBaseInfo.getRoomId());
                    resp.setType(roomBaseInfo.getType());
                    resp.setHot_Flag(roomBaseInfo.getHotFlag());
                    resp.setName(roomBaseInfo.getName());
                    resp.setAvatar(roomBaseInfo.getAvatar());
                    resp.setActiveTime(roomBaseInfo.getActiveTime());
                    // 组装会话列表的文本消息
                    Message message = msgMap.get(roomBaseInfo.getLastMsgId());
                    if(Objects.nonNull(message)){
                        User user = lastMsgUserMap.get(message.getId());
                        AbstractMsgHandler strategyNoNull = MsgHandlerFactory.getStrategyNoNull(message.getType());
                        resp.setText(user.getName() + ":" + strategyNoNull.showContactMsg(message));
                    }
                    //resp.setUnreadCount();
                    return resp;
                }).sorted(Comparator.comparing(ChatRoomResp::getActiveTime).reversed())// 倒序排序
                .collect(Collectors.toList());
    }

    private Map<Long, RoomBaseInfo> getRoomBaseInfoMap(Long uid, List<Long> roomIds) {
        // 获取全部房间信息
        Map<Long, Room> roomMap = roomCache.getBatch(roomIds);
        // 单聊群聊
        Map<Integer, List<Long>> groupRoomIdMap = roomMap.values().stream()
                .collect(Collectors.groupingBy(Room::getType, Collectors.mapping(Room::getId, Collectors.toList())));
        // 获取群组信息
        List<Long> roomGroupIds = groupRoomIdMap.getOrDefault(RoomTypeEnum.GROUP.getType(), new ArrayList<>());
        Map<Long, RoomGroup> roomInfoBatch = roomGroupCache.getBatch(roomGroupIds);
        // 获取好友单聊信息
        List<Long> friendRoomId = groupRoomIdMap.get(RoomTypeEnum.FRIEND.getType());
        Map<Long, User> friendRoomMap = getFriendRoomMap(friendRoomId, uid);
        return roomMap.values().stream()
                .map(room -> {
                    RoomBaseInfo roomBaseInfo = new RoomBaseInfo();
                    roomBaseInfo.setRoomId(room.getId());
                    roomBaseInfo.setType(room.getType());
                    roomBaseInfo.setHotFlag(room.getHotFlag());
                    roomBaseInfo.setLastMsgId(room.getLastMsgId());
                    roomBaseInfo.setActiveTime(room.getActiveTime());
                    if(RoomTypeEnum.of(room.getType()).equals(RoomTypeEnum.GROUP)){
                        RoomGroup roomGroup = roomInfoBatch.get(room.getId());
                        roomBaseInfo.setAvatar(roomGroup.getAvatar());
                        roomBaseInfo.setName(roomGroup.getName());
                    }else if(RoomTypeEnum.of(room.getType()).equals(RoomTypeEnum.FRIEND)) {
                        User user = friendRoomMap.get(room.getId());
                        roomBaseInfo.setAvatar(user.getAvatar());
                        roomBaseInfo.setName(user.getName());
                    }
                    return roomBaseInfo;
                }).collect(Collectors.toMap(RoomBaseInfo::getRoomId, Function.identity()));
    }

    private Map<Long, User> getFriendRoomMap(List<Long> friendRoomId, Long uid) {
        if(CollectionUtils.isEmpty(friendRoomId)){
            return new HashMap<>();
        }
        Map<Long, RoomFriend> roomFriendMap = roomFriendCache.getBatch(friendRoomId);
        Set<Long> friendUidSet = ChatAdapter.getFriendUidSet(roomFriendMap.values(), uid);
        Map<Long, User> userInfoMap = userInfoCache.getBatch(new ArrayList<>(friendUidSet));
        return roomFriendMap.values()
                .stream()
                .collect(Collectors.toMap(RoomFriend::getRoomId, roomFriend -> {
                    Long friendUid = ChatAdapter.getFriendUid(roomFriend, uid);
                    return userInfoMap.get(friendUid);
                }));
    }

    private Double getCursorOrNull(String cursor) {
        return Optional.ofNullable(cursor).map(Double::parseDouble).orElse(null);
    }

    @Override
    public ChatRoomResp getContactDetail(Long uid, Long roomId) {
        Room room = roomCache.get(roomId);
        AssertUtil.isNotEmpty(room, "房间号有误");
        return buildContactResp(uid, Collections.singletonList(roomId)).get(0);
    }

    @Override
    public ChatRoomResp getContactDetailByFriend(Long uid, Long friendUid) {
        RoomFriend friendRoom = roomService.getFriendRoom(uid, friendUid);
        AssertUtil.isNotEmpty(friendRoom, "你们不是好友关系");
        return buildContactResp(uid, Collections.singletonList(friendRoom.getRoomId())).get(0);
    }

    @Override
    public MemberResp getGroupDetail(Long uid, long roomId) {
        RoomGroup roomGroup = roomGroupCache.get(roomId);
        Room room = roomCache.get(roomId);
        AssertUtil.isNotEmpty(roomGroup, "roomId有误");
        Long onlineNum;
        if(isHotGroup(room)){
            onlineNum = userCache.getOnlineNum();
        } else {
            List<Long> memberUidList = groupMemberDao.getMemberUidList(roomGroup.getId());
            onlineNum = userDao.getOnlineCount(memberUidList).longValue();
        }
        GroupRoleAPPEnum groupRole = getGroupRole(uid, room, roomGroup);
        return MemberResp.builder()
                .roomId(roomId)
                .groupName(roomGroup.getName())
                .role(groupRole.getType())
                .avatar(roomGroup.getAvatar())
                .onlineNum(onlineNum)
                .build();
    }

    private GroupRoleAPPEnum getGroupRole(Long uid, Room room, RoomGroup roomGroup) {
        GroupMember member = Objects.isNull(uid) ? null : groupMemberDao.getMember(roomGroup.getId(), uid);
        if(Objects.nonNull(member)){
            return GroupRoleAPPEnum.of(member.getRole());
        } else if(isHotGroup(room)){
            return GroupRoleAPPEnum.MEMBER;
        } else {
            return GroupRoleAPPEnum.REMOVE;
        }
    }

    private boolean isHotGroup(Room room) {
        return HotFlagEnum.YES.getType().equals(room.getHotFlag());
    }

    @Override
    public CursorPageBaseResp<ChatMemberResp> getMemberPage(MemberReq request) {
        Room room = roomCache.get(request.getRoomId());
        AssertUtil.isNotEmpty(room, "房间roomId有误");
        List<Long> memberUidList;
        if(isHotGroup(room)){ // 全员群展示所有人信息
            memberUidList = null;
        } else {
            RoomGroup roomGroup = roomGroupCache.get(request.getRoomId());
            memberUidList = groupMemberDao.getMemberUidList(roomGroup.getId());
        }
        // 获取群成员信息
        return chatService.getMemberPage(memberUidList, request);
    }

    @Override
    public List<ChatMemberListResp> getMemberList(ChatMessageMemberReq request) {
        Room room = roomCache.get(request.getRoomId());
        AssertUtil.isNotEmpty(room, "房间roomId有误");
        if(room.isHotRoom()){// 全员群则展示前1000条
            List<User> memberList = userDao.getMemberList();
            return MemberAdapter.buildMemberList(memberList);
        } else {
            RoomGroup roomGroup = roomGroupCache.get(room.getId());
            List<Long> memberUidList = groupMemberDao.getMemberUidList(roomGroup.getId());
            Map<Long, User> userMap = userInfoCache.getBatch(memberUidList);
            return MemberAdapter.buildMemberList(userMap);
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void delMember(Long uid, MemberDelReq request) {
        Room room = roomCache.get(request.getRoomId());
        AssertUtil.isNotEmpty(room, "房间号有误");
        RoomGroup roomGroup = roomGroupCache.get(request.getRoomId());
        AssertUtil.isNotEmpty(roomGroup, "房间号有误");
        GroupMember self = groupMemberDao.getMember(roomGroup.getId(), uid);
        AssertUtil.isNotEmpty(self, GroupErrorEnum.USER_NOT_IN_GROUP);
        // 1. 判断被移除的人是否是群主或者管理员  （群主不可以被移除，管理员只能被群主移除）
        Long removedUid = request.getUid();
        // 1.1 群主 非法操作，群主不能自己移除自己
        AssertUtil.isTrue(groupMemberDao.isLord(roomGroup.getId(), removedUid), GroupErrorEnum.NOT_ALLOWED_FOR_EXIT_GROUP);
        // 1.2 管理员 判断是否是群主操作
        if(groupMemberDao.isManager(roomGroup.getId(), removedUid)){
            // 群主才能移除管理员
            AssertUtil.isTrue(groupMemberDao.isLord(roomGroup.getId(), uid), GroupErrorEnum.NOT_ALLOWED_OPERATION);
        }
        // 1.3 普通成员 判断是否有权限操作
        AssertUtil.isTrue(hasPower(self), GroupErrorEnum.NOT_ALLOWED_OPERATION);
        GroupMember member = groupMemberDao.getMember(roomGroup.getId(), removedUid);
        AssertUtil.isNotEmpty(member, "该用户已被移除");
        groupMemberDao.removeById(member.getId());
        // 发送移除事件告知群成员
        List<Long> memberUidList = groupMemberDao.getMemberUidList(roomGroup.getId());
        // 发送通知
        WSBaseResp<WSMemberChange> wsBaseResp = WebSocketAdapter.buildMemberRemoveWS(roomGroup.getRoomId(), member.getUid());
        pushService.sendPushMsg(wsBaseResp, memberUidList);
        // 清除缓存
        groupMemberCache.evictMemberUidList(roomGroup.getRoomId());
    }

    private boolean hasPower(GroupMember self) {
        return Objects.equals(self.getRole(), GroupRoleEnum.LEADER.getType())
                || Objects.equals(self.getRole(), GroupRoleEnum.MANAGER.getType())
                || iRoleService.hasPower(self.getUid(), RoleEnum.ADMIN);
    }

    @Override
    @RedissonLock(key = "#request.roomId")
    @Transactional(rollbackFor = Exception.class)
    public void addMember(Long uid, MemberAddReq request) {
        Room room = roomCache.get(request.getRoomId());
        AssertUtil.isNotEmpty(room, "房间roomId有误");
        AssertUtil.isFalse(isHotGroup(room), "全员群无需邀请好友");
        RoomGroup roomGroup = roomGroupCache.get(request.getRoomId());
        AssertUtil.isNotEmpty(roomGroup, "房间号有误");
        GroupMember self = groupMemberDao.getMember(roomGroup.getId(), uid);
        AssertUtil.isNotEmpty(self, "您不是群成员");
        // 把需要拉入的成员与群组先有的成员取差集
        List<Long> memberBatch = groupMemberDao.getmemberBatch(roomGroup.getId(), request.getUidList());
        HashSet<Long> exitsSet = new HashSet<>(memberBatch);
        List<Long> waitAddUidList = memberBatch.stream().filter(a -> !exitsSet.contains(a)).collect(Collectors.toList());
        if (CollectionUtils.isEmpty(waitAddUidList)) {
            return;
        }
        // 添加成员进群组
        List<GroupMember> groupMembers = MemberAdapter.buildMemberAdd(roomGroup.getId(), waitAddUidList);
        groupMemberDao.saveBatch(groupMembers);
        applicationEventPublisher.publishEvent(new GroupMemberAddEvent(this, roomGroup, groupMembers, uid));
    }

    @Override
    @Transactional
    public Long addGroup(Long uid, GroupAddReq request) {
        // 创建房间
        RoomGroup roomGroup = roomService.createGroupRoom(uid);
        // 批量保存群成员
        List<GroupMember> groupMembers = RoomAdapter.buildGroupMemberBatch(roomGroup.getId(), request.getUidList());
        groupMemberDao.saveBatch(groupMembers);
        // 发送消息
        applicationEventPublisher.publishEvent(new GroupMemberAddEvent(this, roomGroup, groupMembers, uid));
        return roomGroup.getRoomId();
    }
}
